/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of Maxsi Client.

	Maxsi Client is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi Client is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
	License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Client. If not, see <http://www.gnu.org/licenses/>.

	Maxsi Client
	A client UI to the digital distribution platform Maxsi Distribution.

	PageReleaseInfo.cpp
	The page that asks the user where the master release image is located and
	information about the Master Server and Content Servers.	

******************************************************************************/

#include "MaxsiLibrary.h"
#include "MaxsiDistribution.h"
#include "UI.h"
#include "PageReleaseInfo.h"

#if 0
#define BLOCK_SIZE (4*1024*1024)
#define SHA1_BUFFER_SIZE (16*1024)

Word_t ThreadReleaseProduct(BYTE* Parameter)
{
	((PageReleaseInfo*)Parameter)->ReleaseProduct();

	return 0;
}

Word_t ThreadReleaseProductSHA(BYTE* Parameter)
{
	((PageReleaseInfo*)Parameter)->ReleaseProductSHA();

	return 0;
}

Word_t ThreadReleaseProductUpload(BYTE* Parameter)
{
	((PageReleaseInfo*)Parameter)->ReleaseProductUpload();

	return 0;
}

size_t UploadCallback(BYTE* UserData, IHTTP* Context, BYTE* Buffer, size_t Available)
{
	// WARNING: Buffer is NOT always null-terminated at Buffer[Available]!
	PrintOutput("%s", Buffer);

	return 0;
}
#endif

PageReleaseInfo::PageReleaseInfo()
{
#if 0
	Release			=	NULL;
	TotalFileSize	=	0;
	FileList		=	NULL;
	FileHashList	=	NULL;
	BlockHashList	=	NULL;
#endif

	MDRelease = (MD_IRelease*)CreateInterface(MD_IRelease_String);
	ReleaseHandle = NULL;
}

PageReleaseInfo::~PageReleaseInfo()
{
	DeleteInterface(MDRelease);
}

MaxsiError PageReleaseInfo::CreateControls()
{
	Release					=	Window->CreateChild(_MESTR("Button"), 125, 32, Window->Width()-1*150, Window->Height()-60, Translate("MaxsiClient/ReleaseProductButton"));
	ProductNameLabel		=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, 25, Translate("MaxsiClient/NameOfYourProduct"));
	ProductNameField		=	Window->CreateChild(_MESTR("TextSingleLine"), Window->Width() - 50, 25, 25, ProductNameLabel->Bottom(), Translate("MaxsiLocalStorage/ReleaseDefaultProductName"));
	MasterServerLabel		=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, ProductNameField->Bottom() + 10, Translate("MaxsiClient/URLOfYourMasterServer"));
	MasterServerField		=	Window->CreateChild(_MESTR("TextSingleLine"), Window->Width() - 50, 25, 25, MasterServerLabel->Bottom(), Translate("MaxsiLocalStorage/ReleaseDefaultMasterServer"));
	ContentServerLabel		=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, MasterServerField->Bottom() + 10, Translate("MaxsiClient/URLsOfYourContentServers"));
	ContentServerField		=	Window->CreateChild(_MESTR("TextSingleLine"), Window->Width() - 50, 25, 25, ContentServerLabel->Bottom(), Translate("MaxsiLocalStorage/ReleaseDefaultContentServer"));
	MasterPathLabel			=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, ContentServerField->Bottom() + 10,Translate("MaxsiClient/LocalPathToYourMasterImage"));
	MasterPathField			=	Window->CreateChild(_MESTR("TextSingleLine"), Window->Width() - 50, 25, 25, MasterPathLabel->Bottom(),Translate("MaxsiLocalStorage/ReleaseDefaultMasterImagePath"));
	HelpInformation			=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 50, 25, MasterPathField->Bottom() + 10, Translate("MaxsiClient/ReleaseProductHelp"));
 
	Cancel					=	Window->CreateChild(_MESTR("Button"), 125, 32, Window->Width()-1*150, Window->Height()-60, Translate("MaxsiClient/ReleaseProductCancel"));
	LabelScanningFolder		=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, 25, Translate("MaxsiClient/ScanningMasterFolder"));
	ProgressScanningFolder	=	Window->CreateChild(_MESTR("ProgressBar"), Window->Width() - 50, 20, 25, LabelScanningFolder->Bottom(), NULL);
	LabelSHA1				=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, ProgressScanningFolder->Bottom() + 10, Translate("MaxsiClient/SHAingMasterFolder"));
	ProgressSHA1			=	Window->CreateChild(_MESTR("ProgressBar"), Window->Width() - 50, 20, 25, LabelSHA1->Bottom(), NULL);
	LabelCompressing		=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, ProgressSHA1->Bottom() + 10, Translate("MaxsiClient/CompressingMasterFolder"));
	ProgressCompressing		=	Window->CreateChild(_MESTR("ProgressBar"), Window->Width() - 50, 20, 25, LabelCompressing->Bottom(), NULL);
	LabelUploading			=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, ProgressCompressing->Bottom() + 10, Translate("MaxsiClient/UploadingMasterFolder"));
	ProgressUploading		=	Window->CreateChild(_MESTR("ProgressBar"), Window->Width() - 50, 20, 25, LabelUploading->Bottom(), NULL);
	LabelMastering			=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 20, 25, ProgressUploading->Bottom() + 10, Translate("MaxsiClient/SendingFileListToMasterServer"));
	ProgressMastering		=	Window->CreateChild(_MESTR("ProgressBar"), Window->Width() - 50, 20, 25, LabelMastering->Bottom(), NULL);
	LabelPleaseWait			=	Window->CreateChild(_MESTR("Label"), Window->Width() - 50, 50, 25, ProgressMastering->Bottom() + 10, Translate("MaxsiClient/PleaseWaitWhileReleasing"));

	State = -1;

	ProgressScanningFolder->SetProgress(0.0f, MAXSI_PROGRESS_NORMAL);
	ProgressSHA1->SetProgress(0.0f, MAXSI_PROGRESS_NORMAL);
	ProgressCompressing->SetProgress(0.0f, MAXSI_PROGRESS_NORMAL);
	ProgressUploading->SetProgress(0.0f, MAXSI_PROGRESS_NORMAL);

	SwitchState(MD_RELEASING_STATE_INFO);

	return MAXSI_ERROR_SUCCESS;
}

void PageReleaseInfo::SwitchState(int NewState)
{
	if ( State == NewState ) { return; }

	if ( NewState != MD_RELEASING_STATE_INFO )
	{
		Release->Hide();
		ProductNameLabel->Hide();
		ProductNameField->Hide();
		MasterServerLabel->Hide();
		MasterServerField->Hide();
		ContentServerLabel->Hide();
		ContentServerField->Hide();
		MasterPathLabel->Hide();
		MasterPathField->Hide();
		HelpInformation->Hide();
	}

	if ( NewState != MD_RELEASING_STATE_RELEASING )
	{
		Cancel->Hide();
		LabelScanningFolder->Hide();
		LabelSHA1->Hide();
		LabelCompressing->Hide();
		LabelUploading->Hide();
		LabelMastering->Hide();
		ProgressScanningFolder->Hide();
		ProgressSHA1->Hide();
		ProgressCompressing->Hide();
		ProgressUploading->Hide();
		ProgressMastering->Hide();
		LabelPleaseWait->Hide();
	}

	if ( NewState == MD_RELEASING_STATE_INFO )
	{
		Release->Show();
		ProductNameLabel->Show();
		ProductNameField->Show();
		MasterServerLabel->Show();
		MasterServerField->Show();
		ContentServerLabel->Show();
		ContentServerField->Show();
		MasterPathLabel->Show();
		MasterPathField->Show();
		HelpInformation->Show();
	}

	if ( NewState == MD_RELEASING_STATE_RELEASING )
	{
		Cancel->Show();
		LabelScanningFolder->Show();
		LabelSHA1->Show();
		LabelCompressing->Show();
		LabelUploading->Show();
		LabelMastering->Show();
		ProgressScanningFolder->Show();
		ProgressSHA1->Show();
		ProgressCompressing->Show();
		ProgressUploading->Show();
		ProgressMastering->Show();
		LabelPleaseWait->Show();

		//ThreadSystem()->CreateThread(ThreadReleaseProduct, (BYTE*)this);
	}

	State	=	NewState;
}

MaxsiError PageReleaseInfo::DeleteControls()
{
	WindowSystem->DeleteWindow(Release);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::OnEvent(MaxsiWindowEvent* Event)
{
	if ( Event->Owner == Release && Event->Type == MAXSI_EVENT_PRESSED )
	{
		SwitchState(MD_RELEASING_STATE_RELEASING);
		return MAXSI_ERROR_SUCCESS;
	}

	if ( Event->Owner == Cancel && Event->Type == MAXSI_EVENT_PRESSED )
	{
		WindowSystem->ShutDown();
		return MAXSI_ERROR_SUCCESS;
	}

	return MAXSI_ERROR_NOT_SUPPORTED;
}

#if 0
MaxsiError PageReleaseInfo::ReleaseProduct()
{
	ScanDir(_MESTR("/home/sortie/md/"), _MESTR(""));

	NumBlocks = TotalFileSize / BLOCK_SIZE;

	ThreadSystem()->CreateThread(ThreadReleaseProductSHA, (BYTE*)this);
	ThreadSystem()->CreateThread(ThreadReleaseProductUpload, (BYTE*)this);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::ReleaseProductSHA()
{
	CalculateFileHashes();
	CalculateFileList();
	UploadFileList();

	return MAXSI_ERROR_SUCCESS;
}


MaxsiError PageReleaseInfo::ReleaseProductUpload()
{
	UploadBlocks();

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::ScanDir(MESTR* AbsPath, MESTR* RelPath)
{
	ThreadSetScanProgress(0.5, MAXSI_PROGRESS_INDETERMINATE);

	MaxsiDirectoryEntry	Folder;

	MaxsiError	Result		=	FileSystem()->OpenFolder(AbsPath, &Folder);

	if ( Result != MAXSI_ERROR_SUCCESS ) { return Result; }

	while ( Result == MAXSI_ERROR_SUCCESS && FileSystem()->ReadFolder(&Folder) == MAXSI_ERROR_SUCCESS )
	{
		if ( MESTRCMP(Folder.FileName, _MESTR(".")) == 0 || MESTRCMP(Folder.FileName, _MESTR("..")) == 0 ) { continue; }

		MESTR* RelFilePath	=	BuildString(2, RelPath, Folder.FileName);

		if ( RelFilePath == NULL ) { PrintOutput("Memory allocation error when searching directory\n"); Result = MAXSI_ERROR_MEM_ALLOC_FAIL; }

		if ( Folder.Type == MAXSI_DIR_ENTRY_UNKNOWN )
		{
			PrintOutput("Ignoring unknown %MS (what is this?)\n", RelFilePath);	
		}
		else if ( Folder.Type == MAXSI_DIR_ENTRY_DIR )
		{
			// This is a recursive function! Let's let the stack play chicken with the filesystem!
			//PrintOutput("Dir\t%MS/\n", RelFilePath);

			MESTR* NewRelPath		=	BuildString(2, RelFilePath, _MESTR("/"));
			MESTR* NewAbsPath		=	BuildString(3, AbsPath, Folder.FileName, _MESTR("/"));

			if ( NewRelPath == NULL || NewAbsPath == NULL )
			{
				PrintOutput("Memory allocation error when searching directory\n");
				Result = MAXSI_ERROR_MEM_ALLOC_FAIL; 
			}
			else
			{
				ScanDir(NewAbsPath, NewRelPath);
			}

			if ( NewRelPath != NULL ) { delete[] NewRelPath; }
			if ( NewAbsPath != NULL ) { delete[] NewAbsPath; }
		}
		else if ( Folder.Type == MAXSI_DIR_ENTRY_FILE )
		{
			MESTR* NewAbsPath		=	BuildString(2, AbsPath, Folder.FileName);
	
			if ( NewAbsPath == NULL )
			{
				PrintOutput("Memory allocation error when searching directory\n");
				Result = MAXSI_ERROR_MEM_ALLOC_FAIL;
			}
			else
			{
				MaxsiHandle FileHandle;
				if ( (FileHandle =FileSystem()->OpenFile(NewAbsPath, 0) ) != NULL )
				{
					FileSize_t FileSize		=	FileSystem()->GetFileSize(FileHandle);
					//PrintOutput("File\t%MS %zu\n", RelFilePath, FileSize);

					if ( AddListEntryM(&FileList, NewAbsPath) == false || AddListEntryM(&FileList, RelFilePath) == false || AddListEntry(&FileList, (BYTE*)&FileSize, sizeof(FileSize)) == false )
					{
						PrintOutput("Memory allocation error when searching directory\n");
						Result = MAXSI_ERROR_MEM_ALLOC_FAIL;				
					}
					else
					{
						TotalFileSize  += FileSize;
					}

					FileSystem()->CloseFile(FileHandle);
				}
				else
				{
					PrintOutput("Ignoring file %MS (could not read)\n", RelFilePath);	
				}
			}

		}
		else
		{
			PrintOutput("Ignoring other %MS (what is this?)\n", RelFilePath);	
		}

		delete[] RelFilePath;
	}

	FileSystem()->CloseFolder(&Folder);

	ThreadSetScanProgress(1.0, MAXSI_PROGRESS_NORMAL);

	return Result;
}

MaxsiError PageReleaseInfo::CalculateFileHashes()
{
	MESTR* TotalFileSizeString	=	PrintFileSizeShort(TotalFileSize);

	if ( TotalFileSizeString == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }	

	size_t NumFileListEntries = GetListSize(FileList);

	size_t CurrentDot		=	0;
	size_t DotsPrinted		=	0;
	size_t NumDots			=	100;

	size_t BlockLeft		=	BLOCK_SIZE;

	fflush(stdout);

	delete[] TotalFileSizeString;

	FileSize_t		AmountHashed	=	0;
	MaxsiHandle		FileHandle		=	NULL;

	BYTE* 	HashBuffer		=	new BYTE[SHA1_BUFFER_SIZE];

	if ( HashBuffer == NULL )  { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	MaxsiHandle	FileSHA		=	CreateSHA1Context();
	MaxsiHandle	BlockSHA	=	CreateSHA1Context();

	for ( size_t I = 0; I < NumFileListEntries; I += 3 )
	{
		MESTR*	AbsPath		=	GetListEntryM(FileList, I);
		MESTR*	RelPath		=	GetListEntryM(FileList, I+1);
		
		if ( (FileHandle = FileSystem()->OpenFile(AbsPath, MAXSI_FILE_READ | MAXSI_FILE_SEQUENTIAL)) != NULL )
		{
			FileSize_t FileSize		=	FileSystem()->GetFileSize(FileHandle);
			FileSize_t FileLeft		=	FileSize;

			while ( FileLeft )
			{
				size_t FragmentSize		=	(FileLeft > SHA1_BUFFER_SIZE) ? SHA1_BUFFER_SIZE : FileLeft;
				size_t FragmentLeft		=	FragmentSize;

				FileSystem()->ReadFile(FileHandle, HashBuffer, FragmentSize);

				// Update the file hash
				UpdateSHA1Context(FileSHA, HashBuffer, FragmentSize);

				size_t FragmentBlockRead	=	0;

				// Update the block hashes
				while ( FragmentLeft )
				{
					size_t BlockFragmentSize	=	FragmentLeft;

					if ( BlockFragmentSize > BlockLeft ) { BlockFragmentSize = BlockLeft; }

					UpdateSHA1Context(BlockSHA, HashBuffer + FragmentBlockRead, BlockFragmentSize);

					BlockLeft			-=	BlockFragmentSize;
					FragmentLeft		-=	BlockFragmentSize;
					FragmentBlockRead	+=	BlockFragmentSize;

					if ( BlockLeft == 0 )
					{
						char* BlockHash	=	ReportSHA1ContextA(BlockSHA);

						AddListEntryA(&BlockHashList, BlockHash);

						ResetSHA1Context(BlockSHA);
						BlockLeft	=	BLOCK_SIZE;
					}
				}

				AmountHashed	+=	FragmentSize;
				FileLeft		-=	FragmentSize;

				// Calculate the progress and display it.
				CurrentDot	=	(NumDots * AmountHashed) / TotalFileSize;

				if ( CurrentDot > DotsPrinted )
				{
					ThreadSetSHAProgress((DotsPrinted / 100.0f), MAXSI_PROGRESS_NORMAL);

					DotsPrinted		=	CurrentDot;
					fflush(stdout);
				}
			}

			FileSystem()->CloseFile(FileHandle);

			char* FileHash	=	ReportSHA1ContextA(FileSHA);

			AddListEntryA(&FileHashList, FileHash);

			ResetSHA1Context(FileSHA);
		}
	}
	
	if ( FileSHA != NULL ) { DeleteSHA1Context(FileSHA); }
	if ( BlockSHA != NULL ) { DeleteSHA1Context(BlockSHA); }

	ThreadSetSHAProgress(1.0, MAXSI_PROGRESS_NORMAL);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::CalculateFileList()
{
	ThreadSetMasteringProgress(0.5, MAXSI_PROGRESS_NORMAL);

	// First convert all the file names to UTF-8.
	
	size_t NumEntries	=	GetListSize(FileList);

	List UTF8FileList	=	CreateList(NumEntries / 3);

	if ( UTF8FileList == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }

	for ( size_t I = 0; I < NumEntries; I += 3 )
	{
		UTF8_t*		UTF8FileName	=	MESTR2UTF8(GetListEntryM(FileList, I + 1));

		if ( UTF8FileName == NULL || SetListEntryA(UTF8FileList, I/3, (char*)UTF8FileName) == false ) { DeleteList(UTF8FileList); return MAXSI_ERROR_MEM_ALLOC_FAIL; }
	}

	// A static memory buffer large enough to hold the largest filesize in base 10.
	UTF8_t	FileSizeString[FILESIZE_MAX_LEN_BASE_10 + 1];

	// Create a buffer assembler that is able to combine all the input we will be giving it.
	BufferAssembler		Assembler		=	CreateBufferAssembler();

	if ( Assembler == NULL ) { DeleteList(UTF8FileList); }	

	// Now that we have a glorious list of UTF-8 encoded filenames, go ahead and burn a bufferful of filenames, filesizes, blocks, and hashes!
	
	for ( size_t I = 0; I < NumEntries; I += 3 )
	{
		// Find the filename of the file.
		UTF8_t*		FileName	=	(UTF8_t*)GetListEntryA(UTF8FileList, I / 3);

		// Find the filesize of the file.
		FileSize_t*	FileSize	=	(FileSize_t*)GetListEntryA(FileList, I + 2);

		// Print the filesize of the file.
		FileSizeString[PrintUInt64A10((char*)FileSizeString, *FileSize)] = 0;

		// Find the SHA-1 hash of the file.
		char*		FileHash	=	GetListEntryA(FileHashList, I/3);

		// Now write data to our buffer assembler!
		WriteBufferDataA(Assembler, "File \"");
		WriteBufferDataU(Assembler, FileName);
		WriteBufferDataA(Assembler, "\" ");
		WriteBufferDataU(Assembler, FileSizeString);
		WriteBufferDataA(Assembler, " \"");
		WriteBufferDataA(Assembler, FileHash);
		WriteBufferDataA(Assembler, "\"\r\n");
	}

	for ( size_t I = 0; I < NumBlocks; I++ )
	{
		// Find the SHA-1 hash of the file.
		char*		BlockHash	=	GetListEntryA(BlockHashList, I);

		// Now write data to our buffer assembler!
		WriteBufferDataA(Assembler, "Block \"");
		WriteBufferDataA(Assembler, BlockHash);
		WriteBufferDataA(Assembler, "\"\r\n");
	}

	FileListString	=	AssembleBuffer(Assembler);

	// Clean up!
	DeleteBufferAssembler(Assembler);
	DeleteList(UTF8FileList);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::UploadFileList()
{
	ThreadSetMasteringProgress(1.0, MAXSI_PROGRESS_INDETERMINATE);

	MESTR*	MasterServerM			=	MasterServerField->GetText();
	char*	MasterServer			=	MESTR2CHAR(MasterServerM);

	// Create a MIME attachment containing the block!
	size_t MyMIMESize;
	BYTE* MyMIME = CreateHTTPMIMEBodyFileAttachment("TheBlock", (BYTE*)FileListString, strlen((char*)FileListString), &MyMIMESize);

	char* URL	=	PrintStringA("%s?action=putfilelist&ContentArchive=at10", MasterServer);

	IHTTP*	HTTP		=	(IHTTP*)CreateInterface("IHTTP");
	HTTP->SetCallback(UploadCallback);
	HTTP->SetCallbackUserData(NULL);
	HTTP->SetNetwork(Networking());
	HTTP->SetVersion(1, 1);
	HTTP->SetMethod((ASCII_t*)"POST");
	HTTP->SetURLA(URL);
	HTTP->SetMimeAttachmentBody(MyMIME, MyMIMESize);

	if ( HTTP->Continue() != MAXSI_ERROR_SUCCESS )
	{
		//PrintOutput("%MS %zu\n", Translate("MaxsiRelease/CouldNotUploadDataBlock"), BlockId);
	}

	PrintOutput("\n");

	DeleteInterface(HTTP);

	delete[] MyMIME;

	ThreadSetMasteringProgress(1.0, MAXSI_PROGRESS_NORMAL);

	delete[] MasterServer;
	delete[] MasterServerM;

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::UploadBlocks()
{
	MESTR* TotalFileSizeString	=	PrintFileSizeShort(TotalFileSize);

	if ( TotalFileSizeString == NULL ) { return MAXSI_ERROR_MEM_ALLOC_FAIL; }	

	size_t NumFileListEntries = GetListSize(FileList);

	size_t CurrentDot		=	0;
	size_t DotsPrinted		=	0;
	size_t NumDots			=	40;

	size_t BlockLeft		=	BLOCK_SIZE;
	size_t BlockId			=	0;

	delete[] TotalFileSizeString;

	FileSize_t		AmountHashed	=	0;
	MaxsiHandle		FileHandle		=	NULL;

	MaxsiCompressionAlgorithmLZMA*	LZMA	=	(MaxsiCompressionAlgorithmLZMA*)CreateInterface("MaxsiCompressionAlgorithmLZMA");

	size_t	CompressedBufferSize	=	LZMA->GetCompressedBound(BLOCK_SIZE);

	BYTE* 	UncompressedBuffer		=	new BYTE[BLOCK_SIZE];
	BYTE* 	CompressedBuffer		=	new BYTE[CompressedBufferSize];

	MESTR*	ContentServerM			=	ContentServerField->GetText();
	char*	ContentServer			=	MESTR2CHAR(ContentServerM);

	for ( size_t I = 0; I < NumFileListEntries; I += 3 )
	{
		MESTR*	AbsPath		=	GetListEntryM(FileList, I);
		MESTR*	RelPath		=	GetListEntryM(FileList, I+1);
		
		if ( (FileHandle = FileSystem()->OpenFile(AbsPath, MAXSI_FILE_READ | MAXSI_FILE_SEQUENTIAL)) != NULL )
		{
			FileSize_t FileSize		=	FileSystem()->GetFileSize(FileHandle);
			FileSize_t FileLeft		=	FileSize;

			while ( FileLeft )
			{
				size_t FragmentSize		=	(FileLeft > BlockLeft) ? BlockLeft : FileLeft;

				FileSystem()->ReadFile(FileHandle, UncompressedBuffer + (BLOCK_SIZE - BlockLeft), FragmentSize);
				
				FileLeft		-=	FragmentSize;
				BlockLeft		-=	FragmentSize;

				if ( ( BlockLeft == 0 || I == NumFileListEntries - 2 ) && BlockLeft != BLOCK_SIZE )
				{
				
					// Compress the block!
					PrintOutput("Compressing Block\t%zu/%zu", BlockId, NumBlocks);
					fflush(stdout);

					ThreadSetCompressProgress((float)BlockId/(float)NumBlocks, MAXSI_PROGRESS_NORMAL);
				
					size_t CompressedSize	=	CompressedBufferSize;
					LZMA->Compress(9, UncompressedBuffer, (BLOCK_SIZE - BlockLeft), CompressedBuffer, &CompressedSize);

					PrintOutput("\nUploading Block\t\t%zu/%zu", BlockId, NumBlocks);
					fflush(stdout);						

					ThreadSetUploadProgress((float)BlockId/(float)NumBlocks, MAXSI_PROGRESS_NORMAL);
	
					// Create a MIME attachment containing the block!
					size_t MyMIMESize;
					BYTE* MyMIME = CreateHTTPMIMEBodyFileAttachment("TheBlock", (BYTE*)CompressedBuffer, CompressedSize, &MyMIMESize);

					char* URL	=	PrintStringA("%s?action=upload&ContentArchive=at10&BlockId=%zu", ContentServer, BlockId, ( BlockId != 0 ));

					IHTTP*	HTTP		=	(IHTTP*)CreateInterface("IHTTP");
					HTTP->SetCallback(UploadCallback);
					HTTP->SetCallbackUserData(NULL);
					HTTP->SetNetwork(Networking());
					HTTP->SetVersion(1, 1);
					HTTP->SetMethod((ASCII_t*)"POST");
					HTTP->SetURLA(URL);
					HTTP->SetMimeAttachmentBody(MyMIME, MyMIMESize);

					if ( HTTP->Continue() != MAXSI_ERROR_SUCCESS )
					{
						//PrintOutput("%MS %zu\n", Translate("MaxsiRelease/CouldNotUploadDataBlock"), BlockId);
					}

					PrintOutput("\n");

					DeleteInterface(HTTP);

					delete[] MyMIME;

					BlockId++;
			
					BlockLeft	=	BLOCK_SIZE;		
				}
			}

			FileSystem()->CloseFile(FileHandle);
		}
	}

	delete[] ContentServer;
	delete[] ContentServerM;

	DeleteInterface(LZMA);

	ThreadSetCompressProgress(1.0, MAXSI_PROGRESS_NORMAL);
	ThreadSetUploadProgress(1.0, MAXSI_PROGRESS_NORMAL);

	return MAXSI_ERROR_SUCCESS;
}

MaxsiError PageReleaseInfo::ThreadSetScanProgress(float Progress, int Type)
{
	MaxsiWindowEvent	ProgressEvent(ProgressScanningFolder, MAXSI_EVENT_SET_PROGRESS, (Word_t)(Progress*100.0f), (Word_t)Type);

	return WindowSystem->PostEventThreaded(&ProgressEvent);
}

MaxsiError PageReleaseInfo::ThreadSetSHAProgress(float Progress, int Type)
{
	MaxsiWindowEvent	ProgressEvent(ProgressSHA1, MAXSI_EVENT_SET_PROGRESS, (Word_t)(Progress*100.0f), (Word_t)Type);

	return WindowSystem->PostEventThreaded(&ProgressEvent);
}

MaxsiError PageReleaseInfo::ThreadSetCompressProgress(float Progress, int Type)
{
	MaxsiWindowEvent	ProgressEvent(ProgressCompressing, MAXSI_EVENT_SET_PROGRESS, (Word_t)(Progress*100.0f), (Word_t)Type);

	return WindowSystem->PostEventThreaded(&ProgressEvent);
}

MaxsiError PageReleaseInfo::ThreadSetUploadProgress(float Progress, int Type)
{
	MaxsiWindowEvent	ProgressEvent(ProgressUploading, MAXSI_EVENT_SET_PROGRESS, (Word_t)(Progress*100.0f), (Word_t)Type);

	return WindowSystem->PostEventThreaded(&ProgressEvent);
}

MaxsiError PageReleaseInfo::ThreadSetMasteringProgress(float Progress, int Type)
{
	MaxsiWindowEvent	ProgressEvent(ProgressMastering, MAXSI_EVENT_SET_PROGRESS, (Word_t)(Progress*100.0f), (Word_t)Type);

	return WindowSystem->PostEventThreaded(&ProgressEvent);
}

#endif

